﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using System.ComponentModel;
using System.Data.Common;
using System.Data;
using System.Globalization;

namespace ResourceBlender_Express {
    /// <summary>
    /// Allows duplication of all translations from one language to another.
    /// </summary>
    public partial class WinCreateVariant : Window {
        private bool success = false;
        /// <summary>
        /// Gets whether the translation was completed successfully.
        /// </summary>
        /// <value><c>true</c> if successful; otherwise, <c>false</c>.</value>
        public bool Success { get { return success; } }

        /// <summary>
        /// Initializes a new <see cref="WinCreateVariant"/>.
        /// </summary>
        public WinCreateVariant() {
            InitializeComponent();
        }

        /// <summary>
        /// Loads the list of unique languages when the window loads.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.RoutedEventArgs"/> instance containing the event data.</param>
        private void Window_Loaded(object sender, RoutedEventArgs e) {
            BackgroundWorker worker = new BackgroundWorker();
            worker.DoWork += (s, dwe) => {
                Database db = new Database();
                try {
                    dwe.Result = db.LoadUniqueLanguages();
                } catch(Exception ex) {
                    dwe.Result = "Error connecting to the database: " + ex.Message;
                } finally {
                    if(db != null) {
                        try {
                            db.CloseConnection();
                        } catch { }
                    }
                }
            };
            worker.RunWorkerCompleted += (s, rwe) => {
                SetStatus(null, false);
                if(rwe.Result is ObservableSortedDictionary<string, CultureInfo>) {
                    ObservableSortedDictionary<string, CultureInfo> languages = rwe.Result as ObservableSortedDictionary<string, CultureInfo>;
                    if(languages.Count == 0) {
                        DialogBox.Show(this, "No resources exist. Before creating a language variant, at least one resource must be added.", "Error", DialogBox.DialogBoxType.OK, DialogBox.DialogBoxIcon.Error);
                        Close();
                    } else {
                        lstSourceLanguage.ItemsSource = languages;
                    }
                } else {
                    DialogBox.Show(this, rwe.Result.ToString(), "Error", DialogBox.DialogBoxType.OK, DialogBox.DialogBoxIcon.Error);
                }
            };
            SetStatus("Loading languages...", true);
            worker.RunWorkerAsync();
            ObservableSortedDictionary<string, CultureInfo> availableLangs = new ObservableSortedDictionary<string, CultureInfo>(new Comparers.CultureInfoEnglshNameComparer());
            foreach(CultureInfo culture in CultureInfo.GetCultures(CultureTypes.AllCultures)) {
                if(culture.Name == "") {
                    continue;
                }
                availableLangs.Add(culture.Name, culture);
            }
            lstDestinationLanguage.ItemsSource = availableLangs;
        }

        /// <summary>
        /// Starts copying between languages.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.Windows.Input.ExecutedRoutedEventArgs"/> instance containing the event data.</param>
        private void CreateVariant(object sender, ExecutedRoutedEventArgs e) {
            if(ValidateSelection()) {
                string sourceLanguage = lstSourceLanguage.SelectedValue.ToString(), 
                       destinationLanguage = lstDestinationLanguage.SelectedValue.ToString();
                bool overwriteExisting = radOverwrite.IsChecked == true;
                BackgroundWorker worker = new BackgroundWorker();
                worker.WorkerReportsProgress = true;
                worker.WorkerSupportsCancellation = true;
                worker.DoWork += (s, dwe) => {
                    StatusMessage.StatusChangedEventHandler logHandler = new StatusMessage.StatusChangedEventHandler(message => {
                        worker.ReportProgress(0, message);
                    });
                    Database db = null;
                    string sourceEnglishName,
                           destinationEnglishName;
                    try {
                        sourceEnglishName = CultureInfo.GetCultureInfo(sourceLanguage).EnglishName;
                    } catch {
                        sourceEnglishName = sourceLanguage;
                    }
                    try {
                        destinationEnglishName = CultureInfo.GetCultureInfo(destinationLanguage).EnglishName;
                    } catch {
                        destinationEnglishName = destinationLanguage;
                    }
                    try {
                        db = new Database();
                        db.OpenConnection();
                        using(DbTransaction trans = db.BeginTransaction()) {
                            using(Database.Command cmd = db.CreateCommand(trans)) {
                                cmd.Transaction = trans;
                                cmd.CommandText = string.Format("SELECT resourceid, translation FROM {0}resources WHERE language = @lang", db.TablePrefix);
                                cmd.AddParameterWithValue("@lang", sourceLanguage);
                                Dictionary<object, object> resources = new Dictionary<object, object>();
                                DbDataReader reader = cmd.ExecuteReader();
                                while(reader.Read()) {
                                    resources.Add(reader[0], reader[1]);
                                }
                                reader.Close();
                                cmd.Parameters["@lang"].Value = destinationLanguage;
                                cmd.AddParameterWithValue("@tID", "");
                                cmd.AddParameterWithValue("@trans", "");
                                cmd.AddParameterWithValue("@comment", "");
                                int numCopied = 0;
                                if(overwriteExisting) {
                                    switch(db.InstanceType) {
                                        case Database.DatabaseType.MySQL:
                                            cmd.CommandText = string.Format("INSERT INTO {0}resources (resourceid, language, translation, translatorcomment) VALUES (@tID, @lang, @trans, @comment) ON DUPLICATE KEY UPDATE translation = @trans, translatorcomment = @comment", db.TablePrefix);
                                            break;
                                        case Database.DatabaseType.SQLServer:
                                            cmd.CommandText = string.Format("UPDATE {0}resources SET translation = @trans, translatorcomment = @comment WHERE resourceid = @tID AND language = @lang; IF @@ROWCOUNT = 0 INSERT INTO {0}resources (resourceid, language, translation, translatorcomment) VALUES (@tID, @lang, @trans, @comment)", db.TablePrefix);
                                            break;
                                        case Database.DatabaseType.SQLite:
                                            cmd.CommandText = string.Format("INSERT OR REPLACE INTO {0}resources (resourceid, language, translation, translatorcomment) VALUES (@tID, @lang, @trans, @comment)", db.TablePrefix);
                                            break;
                                    }
                                    foreach(KeyValuePair<object, object> resource in resources) { // <resource ID, translation>
                                        cmd.Parameters["@tID"].Value = resource.Key;
                                        cmd.Parameters["@trans"].Value = resource.Value;
                                        try {
                                            cmd.ExecuteNonQuery();
                                            logHandler(new StatusMessage("Overwriting translation for resource #" + resource.Key + "...", StatusMessage.StatusMessageType.Success));
                                            numCopied++;
                                        } catch(Exception ex) { // any errors are SQL errors
                                            logHandler(new StatusMessage("Error adding translation for resource #" + resource.Key + ": " + ex.Message, StatusMessage.StatusMessageType.Error));
                                        }
                                        if(worker.CancellationPending) {
                                            throw new Exception("Aborted");
                                        }
                                    }
                                } else {
                                    cmd.CommandText = string.Format("INSERT INTO {0}resources (resourceid, language, translation, translatorcomment) VALUES (@tID, @lang, @trans, @comment)", db.TablePrefix);
                                    foreach(KeyValuePair<object, object> resource in resources) { // <resource ID, translation>
                                        cmd.Parameters["@tID"].Value = resource.Key;
                                        cmd.Parameters["@trans"].Value = resource.Value;
                                        try {
                                            cmd.ExecuteNonQuery();
                                            logHandler(new StatusMessage("Copied translation for resource #" + resource.Key + "...", StatusMessage.StatusMessageType.Success));
                                            numCopied++;
                                        } catch { // errors thrown when a translation exists
                                            logHandler(new StatusMessage("Translation for resource #" + resource.Key + " exists, skipping...", StatusMessage.StatusMessageType.Error));
                                        }
                                    }
                                    if(worker.CancellationPending) {
                                        throw new Exception("Aborted");
                                    }
                                }
                                logHandler(new StatusMessage(string.Format("Copied {0} translations from {1} to {2}", numCopied, sourceEnglishName, destinationEnglishName), StatusMessage.StatusMessageType.Status));
                            }
                            trans.Commit();
                        }
                    } catch(Exception ex) {
                        logHandler(new StatusMessage(ex.Message, StatusMessage.StatusMessageType.Error));
                        throw;
                    } finally {
                        if(db != null) {
                            try {
                                db.CloseConnection();
                            } catch { }
                        }
                    }
                };
                WinWorkerLog dlg = new WinWorkerLog("Copying translations...", worker);
                dlg.Owner = this;
                dlg.ShowDialog();
                if(dlg.Success) {
                    this.success = true;
                    Close();
                }
            }
        }

        /// <summary>
        /// Validates the selection is valid.
        /// </summary>
        /// <returns></returns>
        private bool ValidateSelection() {
            bool valid = true;
            if(lstSourceLanguage.SelectedValue == null) {
                DialogBox.ShowAlert(this, "Source language must be specified", "Error");
                valid = false;
            } else if(lstDestinationLanguage.SelectedValue == null) {
                DialogBox.ShowAlert(this, "Destination language must be specified", "Error");
                valid = false;
            } else if(lstSourceLanguage.SelectedValue == lstDestinationLanguage.SelectedValue) {
                DialogBox.ShowAlert(this, "Source and destination languages must be different", "Error");
                valid = false;
            }
            return valid;
        }

        /// <summary>
        /// Shows the help file.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.Windows.Input.ExecutedRoutedEventArgs"/> instance containing the event data.</param>
        private void ShowHelp(object sender, ExecutedRoutedEventArgs e) {
            Utils.OpenHelp("Creating.Variants.html");
        }

        /// <summary>
        /// Sets the text on the status message.
        /// </summary>
        /// <param name="message">The message.</param>
        /// <param name="showProgress"><c>true</c> if the progress bar should be shown, otherwise <c>false</c>.</param>
        private void SetStatus(string message, bool showProgress) {
            prgStatus.Visibility = showProgress ? Visibility.Visible : Visibility.Hidden;
            if(message == null) {
                lblStatus.Visibility = Visibility.Hidden;
            } else {
                lblStatus.Visibility = Visibility.Visible;
                lblStatus.Content = message;
            }
        }

        /// <summary>
        /// Closes the window.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.Windows.Input.ExecutedRoutedEventArgs"/> instance containing the event data.</param>
        private void CloseWindow(object sender, ExecutedRoutedEventArgs e) {
            Close();
        }
    }
}
